﻿// -----------------------------------------------------------------------
// <copyright file="TableDataDto.cs" company="JSC Centido">
//  Copyright (c) JSC Centido. All rights reserved.
// </copyright>
// -----------------------------------------------------------------------

using System;
using System.Xml;
using System.Xml.Schema;

namespace Centido.Core.Dto
{
    /// <summary>
    ///   A data transfer object for data in a table.
    /// </summary>
    public class TableDataDto : DataTransferObject
    {
        /// <summary>
        ///   The name of the xml tag that is used to store this object.
        /// </summary>
        public const string XmlTag = "tableData";

        /// <summary>
        ///   The current xml schema version.
        /// </summary>
        public const string CurrentXmlSchemaVersion = "1";

        /// <summary>
        ///   The name of the xml attribute to store the table name.
        /// </summary>
        public const string TableNameAttribute = "tableName";

        /// <summary>
        ///   The name of the xml tag to store the row list.
        /// </summary>
        public const string RowListTag = "rows";

        /// <summary>
        ///   The data transfer object for the current table row.
        /// </summary>
        private readonly TableRowDataDto _currentRowDto = new TableRowDataDto();


        /// <summary>
        ///   Gets or sets the table name.
        /// </summary>
        public string TableName
        {
            get;
            set;
        }


        /// <summary>
        ///   Gets or sets the code that writes the table data to xml.
        /// </summary>
        public Action<XmlWriter> WriteHandler
        {
            get;
            set;
        }


        /// <summary>
        ///   Gets or sets the code that reads the table data from xml.
        /// </summary>
        /// <remarks>
        ///   The first argument of the handler stands for the table name,
        /// the second argument - for the current table row.
        /// </remarks>
        public Action<string, TableRowDataDto> ReadHandler
        {
            get;
            set;
        }


        /// <summary>
        ///   This method is reserved and should not be used. When implementing the IXmlSerializable interface, you should return null (Nothing in Visual Basic) from this method, and instead, if specifying a custom schema is required, apply the <see cref="T:System.Xml.Serialization.XmlSchemaProviderAttribute"/> to the class.
        /// </summary>
        /// <returns>
        ///   An <see cref="T:System.Xml.Schema.XmlSchema"/> that describes the XML representation of the object that is produced by the <see cref="M:System.Xml.Serialization.IXmlSerializable.WriteXml(System.Xml.XmlWriter)"/> method and consumed by the <see cref="M:System.Xml.Serialization.IXmlSerializable.ReadXml(System.Xml.XmlReader)"/> method.
        /// </returns>
        public override XmlSchema GetSchema()
        {
            return null;
        }


        /// <summary>
        ///   Generates an object from its XML representation.
        /// </summary>
        /// <param name="reader">
        ///   The <see cref="T:System.Xml.XmlReader"/> stream from which the object is deserialized. 
        /// </param>
        /// <exception cref="ArgumentNullException">Thrown when the value of <paramref name="reader"/> is null.</exception>
        public override void ReadXml(XmlReader reader)
        {
            if (reader == null)
                throw new ArgumentNullException("reader");

            reader.MoveToContent();

            // Reading the xml schema version
            var xmlSchemaVersion = ReadStringAttribute(reader, XmlSchemaVersionTag, isRequired: false);

            // If the xml schema version is not defined
            if (string.IsNullOrEmpty(xmlSchemaVersion)
                // or if it is the current xml schema version
                || xmlSchemaVersion == CurrentXmlSchemaVersion)
            {
                // Reading the table name
                TableName = ReadStringAttribute(reader, TableNameAttribute);

                var isEmptyElement = reader.IsEmptyElement;

                reader.ReadStartElement();

                if (!isEmptyElement)
                {
                    // If the rows were specified
                    if (reader.LocalName == RowListTag)
                    {
                        isEmptyElement = reader.IsEmptyElement;

                        reader.ReadStartElement();

                        if (!isEmptyElement)
                        {
                            // Reading a table row
                            while (reader.LocalName == TableRowDataDto.XmlTag)
                            {
                                _currentRowDto.ReadXml(reader);

                                if (ReadHandler != null)
                                    ReadHandler(TableName, _currentRowDto);
                            }

                            reader.ReadEndElement();
                        }
                    }

                    reader.ReadEndElement();
                }
            }
            else
                throw new ArgumentException(string.Format(
                    "The specified xml schema version is not supported (node = {0}, version = {1}).",
                    reader.Name,
                    xmlSchemaVersion));
        }


        /// <summary>
        ///   Converts an object into its XML representation.
        /// </summary>
        /// <param name="writer">
        ///   The <see cref="T:System.Xml.XmlWriter"/> stream to which the object is serialized. 
        /// </param>
        /// <exception cref="ArgumentNullException">Thrown when the value of <paramref name="writer"/> is null.</exception>
        public override void WriteXml(XmlWriter writer)
        {
            if (writer == null)
                throw new ArgumentNullException("writer");

            writer.WriteStartElement(XmlTag);

            // The xml schema version
            writer.WriteAttributeString(XmlSchemaVersionTag, CurrentXmlSchemaVersion);

            // The table name
            if (!string.IsNullOrEmpty(TableName))
                writer.WriteAttributeString(TableNameAttribute, TableName);

            // The table data
            if (WriteHandler != null)
            {
                writer.WriteStartElement(RowListTag);

                WriteHandler(writer);

                writer.WriteEndElement();
            }

            writer.WriteEndElement();
        }


        /// <summary>
        ///   Creates a data transfer object for the specified table.
        /// </summary>
        /// <param name="table">The specified table.</param>
        /// <exception cref="ArgumentNullException">Thrown when the value of <paramref name="table"/> is null.</exception>
        /// <returns>Returns the data transfer object for the specified security role.</returns>
        public static TableDataDto CreateFrom(Table table)
        {
            if (table == null)
                throw new ArgumentNullException("table");

            var tableDataDto = new TableDataDto
            {
                TableName = table.Name
            };

            return tableDataDto;
        }
    }
}
